{/* Copyright 2020 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import docs from 'docs:@react-aria/overlays';
import focusDocs from 'docs:@react-aria/focus';
import dialogDocs from 'docs:@react-aria/dialog';
import statelyDocs from 'docs:@react-stately/overlays';
import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs';
import {Keyboard} from '@react-spectrum/text';
import packageData from '@react-aria/overlays/package.json';
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';
import Anatomy from './modal-anatomy.svg';

---
category: Overlays
keywords: [overlays, dialog, modal, aria]
---

# useModalOverlay

<PageDescription>{docs.exports.useModalOverlay.description}</PageDescription>

<HeaderInfo
  packageData={packageData}
  componentNames={['useModalOverlay']}
  sourceData={[
    {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/'}
  ]} />

## API

<FunctionAPI function={docs.exports.useModalOverlay} links={docs.links} />

## Features

The HTML [&lt;dialog&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element can be used to build modal overlays. However, it is not yet widely supported across browsers, and can be difficult to style and customize. `useModalOverlay`, helps achieve accessible modal overlays that can be styled as needed.

* **Accessible** – Content outside the modal is hidden from assistive technologies while it is open. The modal optionally closes when interacting outside, or pressing the <Keyboard>Escape</Keyboard> key.
* **Focus management** – Focus is moved into the modal on mount, and restored to the trigger element on unmount. While open, focus is contained within the modal, preventing the user from tabbing outside.
* **Scroll locking** – Scrolling the page behind the modal is prevented while it is open, including in mobile browsers.

**Note**: `useModalOverlay` only handles the overlay itself. It should be combined
with [useDialog](../Modal/useDialog.html) to create fully accessible modal dialogs. Other overlays
such as menus may also be placed in a modal overlay.

## Anatomy

<Anatomy />

A modal overlay consists of an overlay container element, and an underlay. The overlay may contain a [dialog](../Modal/useDialog.html), or another element such as a [menu](../Menu/useMenu.html) or [listbox](../ListBox/useListBox.html) when used within a component such as a [select](../Select/useSelect.html) or [combobox](../ComboBox/useComboBox.html). The underlay is typically a partially transparent element that covers the rest of the screen behind the overlay, and prevents the user from interacting with the elements behind it.

`useModalOverlay` returns props that you should spread onto the overlay and underlay elements:

<TypeContext.Provider value={docs.links}>
  <InterfaceType properties={docs.links[docs.exports.useModalOverlay.return.id].properties} />
</TypeContext.Provider>

State is managed by the <TypeLink links={statelyDocs.links} type={statelyDocs.exports.useOverlayTriggerState} />
hook in `@react-stately/overlays`. The state object should be passed as an argument to `useModalOverlay`.

## Example

This example shows how to build a typical modal dialog, by combining `useModalOverlay` with [useDialog](../Modal/useDialog.html). The `Dialog` component used in this example can also be reused within a [popover](../Popover/usePopover.html) or other types of overlays.

The `Modal` component uses an &lt;<TypeLink links={docs.links} type={docs.exports.Overlay} />&gt; to render its contents in a React [Portal](https://reactjs.org/docs/portals.html) at the end of the document body, which ensures it is not clipped by other elements. It also acts as a focus scope, containing focus within the modal and restoring it to the trigger when it unmounts. <TypeLink links={docs.links} type={docs.exports.useModalOverlay} /> handles preventing page scrolling while the modal is open, hiding content outside the modal from screen readers, and optionally closing it when the user interacts outside or presses the <Keyboard>Escape</Keyboard> key.

```tsx example export=true render=false
import {Overlay, useModalOverlay} from '@react-aria/overlays';
import {useViewportSize} from '@react-aria/utils';

function Modal({state, children, ...props}) {
  let ref = React.useRef(null);
  let {modalProps, underlayProps} = useModalOverlay(props, state, ref);

  return (
    <Overlay>
      <div
        style={{
          position: 'absolute',
          zIndex: 100,
          top: 0,
          left: 0,
          width: '100%',
          height: document.body.clientHeight,
          background: 'rgba(0, 0, 0, 0.5)'
        }}
        {...underlayProps} />
      <div
        style={{
          position: 'fixed',
          top: 0,
          left: 0,
          width: '100%',
          height: useViewportSize().height + 'px',
          zIndex: 101,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center'
        }}>
        <div
          {...modalProps}
          ref={ref}
          style={{
            background: 'var(--page-background)',
            border: '1px solid gray'
          }}>
          {children}
        </div>
      </div>
    </Overlay>
  );
}
```

The below `ModalTrigger` component uses the <TypeLink links={docs.links} type={docs.exports.useOverlayTrigger} /> hook to show the modal when a button is pressed. It accepts a function as children, which is called with a callback that closes the modal. This can be used to implement a close button.

```tsx example export=true render=false
import {useOverlayTriggerState} from '@react-stately/overlays';
import {useOverlayTrigger} from '@react-aria/overlays';

// Reuse the Button from your component library. See below for details.
import {Button} from 'your-component-library';

function ModalTrigger({label, children, ...props}) {
  let state = useOverlayTriggerState(props);
  let {triggerProps, overlayProps} = useOverlayTrigger({type: 'dialog'}, state);

  return <>
    <Button {...triggerProps}>Open Dialog</Button>
    {state.isOpen &&
      <Modal {...props} state={state}>
        {React.cloneElement(children(state.close), overlayProps)}
      </Modal>
    }
  </>;
}
```

Now, we can render an example modal containing a dialog, with a button that closes it using the function provided by `ModalTrigger`.

```tsx example
// Reuse the Dialog from your component library. See below for details.
import {Dialog} from 'your-component-library';

<ModalTrigger label="Open Dialog">
  {close =>
    <Dialog title="Enter your name">
      <form style={{display: 'flex', flexDirection: 'column'}}>
        <label htmlFor="first-name">First Name:</label>
        <input id="first-name" />
        <label htmlFor="last-name">Last Name:</label>
        <input id="last-name" />
        <Button
          onPress={close}
          style={{marginTop: 10}}>
          Submit
        </Button>
      </form>
    </Dialog>
  }
</ModalTrigger>
```

### Dialog

The `Dialog` component is rendered within the `ModalOverlay` component. It is built using the [useDialog](../Modal/useDialog.html) hook, and can also be used in other overlay containers such as [popovers](../Popover/usePopover.html).

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>

```tsx example export=true render=false
import type {AriaDialogProps} from '@react-aria/dialog';
import {useDialog} from '@react-aria/dialog';

interface DialogProps extends AriaDialogProps {
  title?: React.ReactNode,
  children: React.ReactNode
}

function Dialog({title, children, ...props}: DialogProps) {
  let ref = React.useRef(null);
  let {dialogProps, titleProps} = useDialog(props, ref);

  return (
    <div {...dialogProps} ref={ref} style={{padding: 30}}>
      {title &&
        <h3 {...titleProps} style={{marginTop: 0}}>
          {title}
        </h3>
      }
      {children}
    </div>
  );
}
```

</details>

### Button

The `Button` component is used in the above example to toggle the popover. It is built using the [useButton](../Button/useButton.html) hook, and can be shared with many other components.

<details>
  <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show code</summary>

```tsx example export=true render=false
import {useButton} from '@react-aria/button';

function Button(props) {
  let ref = props.buttonRef;
  let {buttonProps} = useButton(props, ref);
  return <button {...buttonProps} ref={ref} style={props.style}>{props.children}</button>;
}
```

</details>

## Usage

The following examples show how to use the `Modal` and `ModalTrigger` components created in the above example.

### Dismissable

If your modal doesn't require the user to make a confirmation, you can set `isDismissable`
on the `Modal`. This allows the user to click outside to close the dialog.

```tsx example
<ModalTrigger isDismissable label="Open Dialog">
  {() =>
    <Dialog title="Notice">
      Click outside to close this dialog.
    </Dialog>
  }
</ModalTrigger>
```

### Keyboard dismiss disabled

By default, modals can be closed by pressing the <Keyboard>Escape</Keyboard> key. This can be disabled with the `isKeyboardDismissDisabled` prop.

```tsx example
<ModalTrigger isKeyboardDismissDisabled label="Open Dialog">
  {close =>
    <Dialog title="Notice">
      <p>You must close this dialog using the button below.</p>
      <Button onPress={close}>Close</Button>
    </Dialog>
  }
</ModalTrigger>
```
